package org.apache.derby.iapi.tools.i18n;

import org.apache.derby.shared.common.i18n.MessageService;

import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.StringTokenizer;

/**
 * @author ex_chenzy59
 * @since 2025/3/5
 *
 */
public final class LocalizedResource {
    private ResourceBundle res;
    private Locale locale;
    private String encode;
    public final static String MESSAGE_FILE = "org.apache.derby.loc.tools.toolsmessages";
    public final static String ENV_CODESET = "derby.ui.codeset";
    public final static String ENV_LOCALE = "derby.ui.locale";
    private String messageFileName;
    private String resourceKey;
    private LocalizedOutput out;
    private LocalizedInput in;
    private boolean enableLocalized;

    private int dateSize;
    private int timeSize;
    private int timestampSize;
    private DateFormat formatDate;
    private DateFormat formatTime;
    private DateFormat formatTimestamp;
    private NumberFormat formatNumber;
    private DecimalFormat formatDecimal;
    private LocalizedResource(){
        init();
    }
    public LocalizedResource(String msgF){
        init(null, null, msgF, true);
    }
    public LocalizedResource(String encStr, String locStr, String msgF){
        init(encStr,locStr,msgF, false);
    }
    public String getTextMessage(String key, Object... objectArr) {
        if (res == null){
            setResource();
        }
        try{
            return MessageFormat.format(res.getString(key), objectArr);
        } catch (Exception e) {
            String tmpFormat = key;
            for (int i=0; objectArr != null && i<objectArr.length; i++)
                tmpFormat = tmpFormat + ", <{" + (i) + "}>";
            return MessageFormat.format(tmpFormat, objectArr);
        }
    }
    public void init(){
        init(null,null,null);
    }
    public void init (String encStr, String locStr, String msgF) {
        init(encStr, locStr, msgF, true);
    }
    private void init (String encStr, String locStr, String msgF, boolean readEnv){
        if (encStr != null){
            encode = encStr;
        }
        //then get encoding string from environment
        if (encode == null && readEnv) {
            String eEncode = getEnvProperty(ENV_CODESET);
            if ( eEncode != null ){
                encode = eEncode;
            }
        }

        // If null at this point then the default encoding
        // will be always used.

        //get locale string from the caller first
        locale = getNewLocale(locStr);

        //if null, get locale again from the environment variable
        if (locale==null && readEnv) {
            String s = getEnvProperty(ENV_LOCALE);
            locale = getNewLocale(s);
        }
        //get the default locale if forced
        if (locale==null){
            locale = Locale.getDefault();
        }
        if (msgF != null) {
            messageFileName = msgF;
        }
        else {
            messageFileName = MESSAGE_FILE;
        }
        //create default in/out
        out = getNewOutput(System.out);
        in = getNewInput(System.in);

        //for faster code: get the format objs
        if (enableLocalized && locale != null){
            formatDecimal = (DecimalFormat)DecimalFormat.getInstance(locale);
            formatNumber = NumberFormat.getInstance(locale);
            formatDate = DateFormat.getDateInstance(DateFormat.LONG,locale);
            formatTime = DateFormat.getTimeInstance(DateFormat.LONG,locale);
            formatTimestamp = DateFormat.getDateTimeInstance(DateFormat.LONG,
                    DateFormat.LONG, locale);
        }
        else {
            formatDecimal = (DecimalFormat)DecimalFormat.getInstance();
            formatNumber = NumberFormat.getInstance();
            formatDate = DateFormat.getDateInstance(DateFormat.LONG);
            formatTime = DateFormat.getTimeInstance(DateFormat.LONG);
            formatTimestamp = DateFormat.getDateTimeInstance(DateFormat.LONG,
                    DateFormat.LONG);
        }
        //initialize display sizes for columns
        initMaxSizes2();
    }
    private final synchronized String getEnvProperty(String key) {
        String s;
        resourceKey =  key;
        s = run();
        //System.out.println("{"+resourceKey+"="+s+"}");
        return s;
    }
    public final String run() {
        return System.getProperty(resourceKey);
    }
    private void setResource(){
        if (res != null){
            return;
        }
        res = MessageService.getBundleWithEnDefault(messageFileName, locale);
    }
    private Locale getNewLocale(String locStr){
        String l="", r="", v="";
        StringTokenizer st;
        if (locStr==null) {
            return null;
        }
        st=new StringTokenizer(locStr, "_");
        try {
            l=st.nextToken();
            if(st.hasMoreTokens())
                r=st.nextToken();
            if(st.hasMoreTokens())
                v=st.nextToken();
            return Locale.of(l,r,v);
        } catch (Exception e) {
            return null;
        }
    }
    public LocalizedOutput getNewOutput(OutputStream o){
        try {
            if (encode != null)
                return new LocalizedOutput(o,encode);
        }
        catch(UnsupportedEncodingException e){
        }
        return new LocalizedOutput(o);
    }
    public LocalizedInput getNewInput(InputStream i) {
        try {
            if (encode != null)
                return new LocalizedInput(i,encode);
        }
        catch (UnsupportedEncodingException e){

        }
        return new LocalizedInput(i);
    }
    private void initMaxSizes2(){
        dateSize = 0;
        timeSize = 0;
        timestampSize = 0;

        int len;

        // check the date & timestamp max length
        // 3900/01/28 !! original devloper thought they were getting 2000/01/28
        Date d = new Date(60907276800000L);
        Timestamp t = new Timestamp(d.getTime());
        for(int month  = 0 ;  month <=11 ; month++, d.setTime(d.getTime() + (30L * 24L * 60L * 60L * 1000L))) {
            len=getDateAsString(d).length();

            if(len > dateSize ) {
                dateSize=len;
            }

            t.setTime(d.getTime() + ((((21L * 60L) + 59L) * 60L) + 59L));
            len=getTimestampAsString(t).length();

            if(len > timestampSize) {
                timestampSize=len;
            }
        }

        // set the time max length
        // minimum of 18 because the old buggy code always used 18
        len = 18;
        for (int hour = 0 ; hour < 24; hour++) {

            long secs = (hour * 3600L) + (59 * 60L) + 59L;

            long ms = secs * 1000L;

            Date td = new Date(ms);

            String fd = formatTime.format(td);

            if (fd.length() > len)
                len = fd.length();
        }
        timeSize=len;

    }
    public String getTimestampAsString(Timestamp t){
        if (!enableLocalized){
            return t.toString();
        }
        return formatTimestamp.format
                (t, new StringBuffer(),
                        new java.text.FieldPosition(0)).toString();
    }
    public String getDateAsString(Date d){
        if (!enableLocalized){
            return d.toString();
        }
        return formatDate.format(d);
    }
}
